package com.letoken.platform.pub.util;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.letoken.platform.pub.enums.LetokenApiResponseEnum;
import com.letoken.platform.pub.exception.ApiServiceException;
import com.letoken.platform.pub.res.web3.BscBalance;
import com.letoken.platform.pub.res.web3.BscResponse;
import com.letoken.platform.pub.res.web3.BscTransaction;

import com.letoken.platform.pub.res.token.TokenErc20TxRes;
import com.letoken.platform.pub.res.token.TokenTxRes;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeDecoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.utils.Numeric;
import org.web3j.crypto.*;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.response.*;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.Transfer;
import org.web3j.utils.Convert;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.*;

/**
 * @author letokenlabs
 * @date 2022/11/16$
 */
@Slf4j
@Component
public class EthUtil {

    public static final BigDecimal ETH_DECIMALS = new BigDecimal(1_000_000_000_000_000_000L);

    public static final BigInteger ETH_GAS_LIMIT = new BigInteger("100000");

    private static String url;
    private static String goerliUrl;
    private static String apikey;

    private static String proxyHost;
    private static boolean enableProxy;

    @Value("${etherscan.api.url:https://api.etherscan.io/api}")
    private String _url;

    @Value("${etherscan.testnet.api.url:https://api-goerli.etherscan.io/api}")
    private String _goerliUrl;

    @Value("${etherscan.api.apikey:}")
    private String _apikey;

    @Value("${etherscan.api.proxyHost:http://127.0.0.1:1080}")
    private String _proxyHost;

    @Value("${etherscan.api.proxy:false}")
    private boolean _enableProxy;

    @PostConstruct
    public void init() {
        url = _url;
        goerliUrl = _goerliUrl;
        apikey = _apikey;

        proxyHost = _proxyHost;
        enableProxy = _enableProxy;
    }

    /**
     * 校验EVM地址
     * @param input
     * @return
     */
    public static boolean checkAddressChecksum(String input) {
        if (StrUtil.isEmpty(input) || !input.startsWith("0x"))
            return false;
        return isValidAddress(input);
    }

    public static boolean isValidAddress(String input) {
        String cleanInput = Numeric.cleanHexPrefix(input);

        try {
            Numeric.toBigIntNoPrefix(cleanInput);
        } catch (NumberFormatException e) {
            return false;
        }

        return cleanInput.length() == 40;
    }

    private static String getApiUrl(String network) {
        String api = "";
        switch (network) {
            case "eth":
            case "1":
                api = url;
                break;
            case "goerli":
            case "5":
                api = goerliUrl;
                break;
            case "sepolia":
            case "11155111":
                api = "https://api-sepolia.etherscan.io/api";
                break;
        }
        return api;
    }

    private static HttpHost getProxy() {
        return enableProxy ? HttpHost.create(proxyHost) : null;
    }

    /**
     * 获取Web3j对象（支持代理）
     * @param endpoint
     * @return
     */
    public static Web3j getWeb3j(String endpoint) {
        if (enableProxy) {
            return Web3j.build(new HttpService(endpoint, HttpClientUtil.getProxyClient(proxyHost)));
        }
        else {
            return Web3j.build(new HttpService(endpoint));
        }
    }

    /**
     *  获取最新区块高度
     * @param network 网络名称或ID(bsc/bsctestnet,56/97)
     * @return
     */
    public static long blockHeight(String network) {

        HashMap<String, String> parame = new HashMap<>();
        parame.put("module", "block");
        parame.put("action", "getblocknobytime");
        parame.put("timestamp", DateUtil.getUnixTimestamp());
        parame.put("closest", "before");
        parame.put("apikey", apikey);
        String res = null;
        try {
            res = HttpClientUtil.sendGet(getApiUrl(network), parame, null, getProxy());
        } catch (Exception e) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_TX_001);
        }
        BscResponse bscResponse = JSONObject.parseObject(res, BscResponse.class);
        if (bscResponse.getStatus().equals("1")) {
            return Long.parseLong(bscResponse.getResult());
        } else {
            throw new ApiServiceException(bscResponse.getMessage());
        }
    }

    /**
     * 根据交易hash获取交易状态
     * @param network 网络名称或ID(bsc/bsctestnet,56/97)
     * @return
     */
    public static String getTxReceiptstatus(String txhash, String network) {
        Boolean flag = false;
        HashMap<String, String> parame = new HashMap<>();
        parame.put("module", "transaction");
        parame.put("action", "gettxreceiptstatus");
        parame.put("txhash", txhash);
        parame.put("apikey", apikey);
        String res = null;
        try {
            res = HttpClientUtil.sendGet(getApiUrl(network), parame, null, getProxy());
        } catch (Exception e) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_TX_001);
        }
        BscResponse bscResponse = JSONObject.parseObject(res, BscResponse.class);
        if (bscResponse.getStatus().equals("1")) {
            JSONObject result = JSONObject.parseObject(bscResponse.getResult());
            return result.get("status").toString();
        } else {
            throw new ApiServiceException(bscResponse.getMessage());
        }
    }

    /**
     * 获取单个地址的ETH余额
     *
     * @param address 钱包地址
     * @param network 网络名称或ID(bsc/bsctestnet,56/97)
     * @return wei
     */
    public static String getBalance(String address, String network) {
        HashMap<String, String> parame = new HashMap<>();
        parame.put("module", "account");
        parame.put("action", "balance");
        parame.put("address", address);
        parame.put("apikey", apikey);
        String res = null;
        try {
            res = HttpClientUtil.sendGet(getApiUrl(network), parame, null, getProxy());
        } catch (Exception e) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_TX_001);
        }
        BscResponse bscResponse = JSONObject.parseObject(res, BscResponse.class);
        if (bscResponse.getStatus().equals("1")) {
            return bscResponse.getResult();
        } else {
            throw new ApiServiceException(bscResponse.getMessage());
        }
    }


    /**
     * 获取多个地址的ETH余额
     * @param addressList 钱包地址(逗号分隔)
     * @param network 网络名称或ID(bsc/bsctestnet,56/97)
     * @return
     */
    public static List<BscBalance> getBalanceMulti(String addressList, String network) {
        HashMap<String, String> parame = new HashMap<>();
        parame.put("module", "account");
        parame.put("action", "balancemulti");
        parame.put("address", addressList);
        parame.put("tag", "latest");
        parame.put("apikey", apikey);
        String res = null;
        try {
            res = HttpClientUtil.sendGet(getApiUrl(network), parame, null, getProxy());
        } catch (Exception e) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_TX_001);
        }
        BscResponse bscResponse = JSONObject.parseObject(res, BscResponse.class);
        List<BscBalance> bscBalances = null;
        if (bscResponse.getStatus().equals("1")) {
            bscBalances = JSONObject.parseObject(bscResponse.getResult(), List.class);
        } else {
            throw new ApiServiceException(bscResponse.getMessage());
        }
        return bscBalances;
    }

    /**
     * 获取钱包代币余额（erc20)
     * @param tokenAddress 代币合约地址
     * @param address 用户钱包地址
     * @return wei
     */
    public static String getTokenBalance(String tokenAddress, String address, String network) {
        HashMap<String, String> parame = new HashMap<>();
        parame.put("module", "account");
        parame.put("action", "tokenbalance");
        parame.put("contractaddress", tokenAddress);
        parame.put("address", address);
        parame.put("tag", "latest");
        parame.put("apikey", apikey);
        String res = null;
        try {
            res = HttpClientUtil.sendGet(getApiUrl(network), parame, null, getProxy());
        } catch (Exception e) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_TX_001);
        }
        BscResponse bscResponse = JSONObject.parseObject(res, BscResponse.class);
        if (bscResponse.getStatus().equals("1")) {
            return bscResponse.getResult();
        } else {
            throw new ApiServiceException(bscResponse.getMessage());
        }
    }

    /**
     * 获取钱包Hash Internal记录
     * @param txhash txhash
     * @param network network
     * @return wei
     */
    public static List<BscTransaction> getTxListInternal(String txhash, String network) {
        Map<String, String> parame = new HashMap<>();
        parame.put("module", "account");
        parame.put("action", "txlistinternal");
        parame.put("txhash", txhash);
        parame.put("apikey", apikey);
        String res = null;
        try {
            res = HttpClientUtil.sendGet(getApiUrl(network), parame, null, getProxy());
        } catch (Exception e) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_TX_001);
        }
        BscResponse bscResponse = JSONObject.parseObject(res, BscResponse.class);
        List<BscTransaction> bscTransaction = null;
        if (bscResponse.getStatus().equals("1")) {
            bscTransaction = JSONObject.parseObject(bscResponse.getResult(), List.class);
        } else {
            throw new ApiServiceException(bscResponse.getMessage());
        }
        return bscTransaction;
    }

    /**
     * 获取GasPrice
     * @param network 网络名称或ID(bsc/bsctestnet,56/97)
     * @return wei
     */
    public static BigInteger getGasPrice(String network) {
        HashMap<String, String> parame = new HashMap<>();
        parame.put("module", "gastracker");
        parame.put("action", "gasoracle");
        parame.put("apikey", apikey);
        String res = null;
        try {
            res = HttpClientUtil.sendGet(url, parame, null, getProxy());
        } catch (Exception e) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_TX_001);
        }
        BscResponse bscResponse = JSONObject.parseObject(res, BscResponse.class);
        if (bscResponse.getStatus().equals("1")) {
            JSONObject result = JSONObject.parseObject(bscResponse.getResult());
            return new BigInteger(result.get("ProposeGasPrice").toString()).multiply(BigInteger.valueOf(10).pow(9));
        } else {
            throw new ApiServiceException(bscResponse.getMessage());
        }
    }

    /**
     * 发送原生代币
     * @param privateKey 钱包秘钥
     * @param toAddress 收款钱包地址
     * @param value 转账数量(wei)
     * @return txHash
     * @throws Exception
     */
    public static String transfer(Web3j web3j, String privateKey, String toAddress, BigDecimal value) throws Exception {

        if (web3j != null) {
            try {
                Credentials credentials = Credentials.create(privateKey);
                TransactionReceipt transactionReceipt = Transfer.sendFunds(web3j, credentials, toAddress, value, Convert.Unit.WEI).send();
                return transactionReceipt.getTransactionHash();
            } catch (Throwable t) {
                log.error(String.format("发送ETH失败 from:%s to:%s amount-eth:%s", getAddressByPrivateKey(privateKey), toAddress, toEth(value.toBigInteger()).toString()));
            }
        }
        return null;
    }

    public static String transferToken(Web3j web3j, String contractAddress, String privateKey, String toAddress, BigDecimal value, BigInteger gasPrice, BigInteger gasLimit) {

        if (web3j != null) {
            String from = getAddressByPrivateKey(privateKey);
            log.info(String.format("Start:SendErc20 from:%s to:%s amount:%s erc20:%s", from, toAddress, value.toString(), contractAddress));
            try {
                //加载转账所需的凭证，用私钥
                Credentials credentials = Credentials.create(privateKey);
                //获取nonce，交易笔数
                BigInteger nonce = getNonce(web3j, from);
                if (nonce == null) {
                    log.error(String.format("END:GetNonceError from:%s to:%s amount:%s erc20:%s", from, toAddress, value.toString(), contractAddress));
                    return null;
                }

                if (gasPrice.equals(0)) {
                    log.error(String.format("END:GetGasPriceError from:%s to:%s amount:%s erc20:%s", from, toAddress, value.toString(), contractAddress));
                    return null;
                }

                //ERC20代币合约方法
                Function function = new Function(
                        "transfer",
                        Arrays.asList(new Address(toAddress), new Uint256(value.toBigInteger())),
                        Collections.singletonList(new TypeReference<Type>() {
                        }));

                //创建RawTransaction交易对象
                String encodedFunction = FunctionEncoder.encode(function);
                RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit,
                        contractAddress, encodedFunction);

                //签名Transaction
                byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
                String hexValue = Numeric.toHexString(signMessage);
                //发送交易
                EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();
                String hash = ethSendTransaction.getTransactionHash();
                if (hash != null) {
                    return hash;
                }
                log.error(String.format("END:HashIsNull from:%s to:%s amount:%s erc20:%s >> %s",
                        from, toAddress, value.toString(), contractAddress, ethSendTransaction.getError().getMessage()));
            } catch (Throwable t) {
                log.error(String.format("发送ERC20失败 from=%s to=%s erc20=%s amount=%s >> %s",
                        from, toAddress, contractAddress, value.toString(), t.getMessage()), t);
            }
        }
        return null;
    }

    /**
     * 发送erc20
     *
     * @param web3j
     * @param contractAddress 合约地址
     * @param privateKey      私钥
     * @param to              收款地址
     * @param value           额度
     * @return
     */
    public static String sendErc20(Web3j web3j, String contractAddress, String privateKey,
                                   String to, BigInteger value) {
        String from = getAddressByPrivateKey(privateKey);
        log.info(String.format("Start:SendErc20 from:%s to:%s amount:%s erc20:%s", from, to, value.toString(), contractAddress));
        try {
            //加载转账所需的凭证，用私钥
            Credentials credentials = Credentials.create(privateKey);
            //获取nonce，交易笔数
            BigInteger nonce = getNonce(web3j, from);
            if (nonce == null) {
                log.error(String.format("END:GetNonceError from:%s to:%s amount:%s erc20:%s", from, to, value.toString(), contractAddress));
                return null;
            }
            //gasPrice和gasLimit 都可以手动设置
            BigInteger gasPrice = getGasPrice(web3j);
            if (gasPrice == null) {
                log.error(String.format("END:GetGasPriceError from:%s to:%s amount:%s erc20:%s", from, to, value.toString(), contractAddress));
                return null;
            }
            //BigInteger.valueOf(4300000L) 如果交易失败 很可能是手续费的设置问题
            BigInteger gasLimit = BigInteger.valueOf(60000L);
            //ERC20代币合约方法
            Function function = new Function(
                    "transfer",
                    Arrays.asList(new Address(to), new Uint256(value)),
                    Collections.singletonList(new TypeReference<Type>() {
                    }));
            //创建RawTransaction交易对象
            String encodedFunction = FunctionEncoder.encode(function);
            RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit,
                    contractAddress, encodedFunction);

            //签名Transaction
            byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
            String hexValue = Numeric.toHexString(signMessage);
            //发送交易
            EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();
            String hash = ethSendTransaction.getTransactionHash();
            if (hash != null) {
                return hash;
            }
            log.error(String.format("END:HashIsNull from:%s to:%s amount:%s erc20:%s", from, to, value.toString(), contractAddress));
        } catch (Throwable t) {
            log.error(String.format("发送ERC20失败 from=%s to=%s erc20=%s amount=%s",
                    from, to, contractAddress, value.toString()), t);
        }
        return null;
    }

    /**
     * 根据私钥获取地址
     *
     * @param privateKey
     * @return
     */
    public static String getAddressByPrivateKey(String privateKey) {
        ECKeyPair ecKeyPair = ECKeyPair.create(new BigInteger(privateKey, 16));
        return "0x" + Keys.getAddress(ecKeyPair).toLowerCase();
    }

    /**
     * 查询地址以太坊数量
     *
     * @param web3j
     * @param address 查询地址
     * @return
     */
    public static BigDecimal balanceOf(Web3j web3j, String address) {
        try {
            EthGetBalance balance = web3j.ethGetBalance(address, DefaultBlockParameterName.LATEST).send();
            BigInteger amount = balance.getBalance();
            if (amount == null || amount.compareTo(BigInteger.ZERO) <= 0) {
                return BigDecimal.ZERO;
            }
            return new BigDecimal(amount).divide(ETH_DECIMALS, 18, RoundingMode.DOWN);
        } catch (Throwable t) {
            log.error(String.format("获取以太坊数量出错 %s", address), t);
        }
        return BigDecimal.ZERO;
    }


    /**
     * 转换成最小单位 Wei
     *
     * @param ethAmount
     * @return
     */
    public static BigInteger toWei(BigDecimal ethAmount) {
        return ethAmount.multiply(ETH_DECIMALS).toBigInteger();
    }

    /**
     * 转换成最小单位 Wei
     *
     * @param ethAmount
     * @return
     */
    public static BigDecimal toWei2(BigDecimal ethAmount) {
        return ethAmount.multiply(ETH_DECIMALS);
    }

    /**
     * wei to eth
     *
     * @param wei
     * @return
     */
    public static BigDecimal toEth(BigInteger wei) {
        return new BigDecimal(wei).divide(ETH_DECIMALS, 18, RoundingMode.DOWN);
    }

    public static BigDecimal toEthScale(BigInteger wei, int scale) {
        return new BigDecimal(wei).divide(ETH_DECIMALS, scale, RoundingMode.DOWN);
    }

    /**
     * 查询erc20的余额
     *
     * @param web3j
     * @param contract 合约地址
     * @param address  查询地址
     * @return
     */
    public static BigInteger balanceOfErc20(Web3j web3j, String contract, String address) {
        try {
            final String DATA_PREFIX = "0x70a08231000000000000000000000000";

            String value = web3j.ethCall(org.web3j.protocol.core.methods.request.Transaction.createEthCallTransaction(address,
                    contract, DATA_PREFIX + address.substring(2)), DefaultBlockParameterName.PENDING).send().getValue();
            if (StrUtil.isEmptyIfStr(value)) {
                return BigInteger.ZERO;
            }
            return new BigInteger(value.substring(2), 16);
        } catch (Throwable t) {
            log.error(String.format("查询ERC20失败 contract:%s address:%s", contract, address), t);
        }
        return BigInteger.ZERO;
    }

    /**
     * 获取gas-price
     *
     * @param web3j
     * @return
     */
    public static BigInteger getGasPrice(Web3j web3j) {
        try {
            EthGasPrice ethGasPrice = web3j.ethGasPrice().sendAsync().get();
            if (ethGasPrice == null) {
                log.error("GetGasPriceError");
                return null;
            }
            return ethGasPrice.getGasPrice();
        } catch (Throwable t) {
            log.error(t.getMessage(), t);
        }
        return null;
    }

    /**
     * 获取nonce
     *
     * @param web3j
     * @param address
     * @return
     */
    public static BigInteger getNonce(Web3j web3j, String address) {
        try {
            EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(address, DefaultBlockParameterName.PENDING).send();
            if (ethGetTransactionCount == null) {
                log.error("GetNonceError:" + address);
                return null;
            }
            return ethGetTransactionCount.getTransactionCount();
        } catch (Throwable t) {
            log.error("GetNonceError:" + address);
        }
        return null;
    }

    /**
     * 发送以太坊
     *
     * @param web3j
     * @param privateKey 发送者私钥
     * @param to         收款地址
     * @param wei        wei为单位的数量
     * @param gasPrice   gas-price
     * @param gasLimit   gas-limit
     * @return
     */
    public static String sendEth(Web3j web3j, String privateKey, String to, BigInteger wei, BigInteger gasPrice, BigInteger gasLimit) {
        String from = getAddressByPrivateKey(privateKey);
        try {
            //加载转账所需的凭证，用私钥
            Credentials credentials = Credentials.create(privateKey);
            //获取nonce，交易笔数
            BigInteger nonce = getNonce(web3j, from);
            //创建RawTransaction交易对象
            RawTransaction rawTransaction = RawTransaction.createEtherTransaction(nonce, gasPrice, gasLimit, to, wei);
            //签名Transaction，这里要对交易做签名
            byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
            String hexValue = Numeric.toHexString(signMessage);
            //发送交易
            EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();
            return ethSendTransaction.getTransactionHash();
        } catch (Throwable t) {
            log.error(String.format("发送ETH失败 from:%s to:%s amount-eth:%s", from, to, toEth(wei).toString()));
        }
        return null;
    }

    /**
     * 发送以太坊
     *
     * @param web3j
     * @param privateKey 发送者私钥
     * @param to         收款地址
     * @param wei        wei为单位的数量
     * @return
     */
    public static String sendEth(Web3j web3j, String privateKey, String to, BigInteger wei) {
        return sendEth(web3j, privateKey, to, wei, getGasPrice(web3j), ETH_GAS_LIMIT);
    }

    /**
     * 发送以太坊
     *
     * @param web3j
     * @param privateKey 发送者私钥
     * @param to         收款地址
     * @param eth        wei为单位的数量
     * @param gasPrice   gas-price
     * @param gasLimit   gas-limit
     * @return
     */
    public static String sendEth(Web3j web3j, String privateKey, String to, BigDecimal eth, BigInteger gasPrice, BigInteger gasLimit) {
        return sendEth(web3j, privateKey, to, toWei(eth), gasPrice, gasLimit);
    }

    /**
     * 发送以太坊
     *
     * @param web3j
     * @param privateKey 发送者私钥
     * @param to         收款地址
     * @param eth        wei为单位的数量
     * @return
     */
    public static String sendEth(Web3j web3j, String privateKey, String to, BigDecimal eth) {
        return sendEth(web3j, privateKey, to, toWei(eth), getGasPrice(web3j), ETH_GAS_LIMIT);
    }

    /**
     * 根据hash获取交易信息
     *
     * @param web3j
     * @param hash
     * @return
     */
    public static EthTransaction getTransaction(Web3j web3j, String hash) {
        try {
            EthTransaction tx = web3j.ethGetTransactionByHash(hash).send();
            return tx;
        } catch (Throwable t) {
            log.error("GetTransactionError:" + hash, t);
        }
        return null;
    }

    public static TransactionReceipt getTransactionReceipt(Web3j web3j, String hash) {
        try {
            EthGetTransactionReceipt receipt = web3j.ethGetTransactionReceipt(hash).send();
            return receipt.getResult();
        } catch (Throwable t) {
            log.error("GetTransactionError:" + hash, t);
        }
        return null;
    }


    public static List<Log> getErc20TransferLogs(Web3j web3j, String hash) {
        try {
            EthGetTransactionReceipt receipt = web3j.ethGetTransactionReceipt(hash).send();
            return receipt.getResult().getLogs();
        } catch (Throwable t) {
            log.error("getErc20TransferValue:" + hash, t);
        }
        return null;
    }

    public static BigInteger getErc20TransferValue(Web3j web3j, String hash) {
        try {
            EthGetTransactionReceipt receipt = web3j.ethGetTransactionReceipt(hash).send();
            String data = receipt.getResult().getLogs().get(0).getData();
            return TypeDecoder.decodeNumeric(data,Uint256.class).getValue();
        } catch (Throwable t) {
            log.error("getErc20TransferValue:" + hash, t);
        }
        return null;
    }

    public static List<TokenErc20TxRes> getTokenErc20Txs(String network, String contractAddress, String walletAddress, Integer page, Integer pageSize, String sort) {
        Map<String, String> parame = new HashMap<>();
        parame.put("module", "account");
        parame.put("action", "tokentx");
        parame.put("contractaddress", contractAddress);
        parame.put("address", walletAddress);
        parame.put("page", page.toString());
        parame.put("offset", pageSize.toString());
        parame.put("sort", sort);
        parame.put("apikey", apikey);
        String res = null;
        try {
            res = HttpClientUtil.sendGet(getApiUrl(network), parame, null, getProxy());
        } catch (Exception e) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_TX_001);
        }
        BscResponse bscResponse = JSONObject.parseObject(res, BscResponse.class);
        List<TokenErc20TxRes> list = null;
        if (bscResponse.getStatus().equals("1")) {
            list = JSONObject.parseArray(bscResponse.getResult(), TokenErc20TxRes.class);
        } else {
            throw new ApiServiceException(bscResponse.getMessage());
        }
        return list;
    }

    public static List<TokenTxRes> getTokenTxs(String network, String walletAddress, Integer page, Integer pageSize, String sort) {
        Map<String, String> parame = new HashMap<>();
        parame.put("module", "account");
        parame.put("action", "txlist");
        parame.put("address", walletAddress);
        parame.put("page", page.toString());
        parame.put("offset", pageSize.toString());
        parame.put("sort", sort);
        parame.put("apikey", apikey);
        String res = null;
        try {
            res = HttpClientUtil.sendGet(getApiUrl(network), parame, null, getProxy());
        } catch (Exception e) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_TX_001);
        }
        BscResponse bscResponse = JSONObject.parseObject(res, BscResponse.class);
        List<TokenTxRes> list = null;
        if (bscResponse.getStatus().equals("1")) {
            list = JSONObject.parseArray(bscResponse.getResult(), TokenTxRes.class);
        } else {
            throw new ApiServiceException(bscResponse.getMessage());
        }
        return list;
    }
}
